Upgrade rust/crates/combine to 4.6.1 Test: make Change-Id: I2154f778e17a26eb25fde1d738162d257f3d9613 
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..8ace92c --- /dev/null +++ b/.cargo_vcs_info.json 
@@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "cd35e54a4e9c555da87984fa665542e31f04e306" + } +} 
diff --git a/.clog.toml b/.clog.toml new file mode 100644 index 0000000..2206c2f --- /dev/null +++ b/.clog.toml 
@@ -0,0 +1,6 @@ +[clog] +repository = "https://github.com/Marwes/combine" + +changelog = "CHANGELOG.md" + +from-latest-tag = true 
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d66f83 --- /dev/null +++ b/.gitignore 
@@ -0,0 +1,8 @@ +/target + + +#vim temporary files +*.swp +*.swo +/.vscode +/benches/small.mp4 
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..fa632ea --- /dev/null +++ b/.travis.yml 
@@ -0,0 +1,37 @@ +language: rust +cache: cargo +sudo: required +dist: trusty +addons: + apt: + packages: + - libcurl4-openssl-dev + - libelf-dev + - libdw-dev + - libssl-dev +rust: +- nightly +- beta +- stable +- 1.40.0 +before_script: +- | + export PATH=$HOME/.local/bin:$PATH +script: +- ./travis.sh +after_success: | + if [[ "$TRAVIS_RUST_VERSION" == stable ]]; then + bash <(curl https://raw.githubusercontent.com/xd009642/tarpaulin/master/travis-install.sh) + cargo tarpaulin --run-type Tests --run-type DocTests --ciserver travis-ci --coveralls $TRAVIS_JOB_ID + fi +env: + global: + - TRAVIS_CARGO_NIGHTLY_FEATURE="" + - secure: Z0JCbroitF6pKdImGLcar9UcXDFUoggvEwYsksoGX16/28iBXLmBX6DDWN1brVdasx/i5M5aEy8xbzcV680+HEbbUgz5uLAMp3xQFzu5FJ276PM9ZFZZgb02EJuYz9THfrC9ajlc+CirYF91i/yMZbpBGajmAzp61puRph/CgI8= +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/ee4400ef3d920e51415e + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always 
diff --git a/Android.bp b/Android.bp index 04f06c3..444ea9f 100644 --- a/Android.bp +++ b/Android.bp 
@@ -1,8 +1,6 @@  // This file is generated by cargo2android.py --run --device.  // Do not modify this file as changes will be overridden on upgrade.   - -  package {  default_applicable_licenses: ["external_rust_crates_combine_license"],  } @@ -25,10 +23,11 @@  host_supported: true,  crate_name: "combine",  cargo_env_compat: true, - cargo_pkg_version: "4.6.0", + cargo_pkg_version: "4.6.1",  srcs: ["src/lib.rs"],  edition: "2018",  features: [ + "alloc",  "bytes",  "default",  "std", 
diff --git a/CHANGELOG.md b/CHANGELOG.md index a71baa1..7005dbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md 
@@ -1,3 +1,14 @@ +<a name="v4.6.1"></a> +### v4.6.1 (2021-08-25) + + +#### Performance + +* Avoid a saturating add in slice_uncons_while ([7f330b0c](https://github.com/Marwes/combine/commit/7f330b0cacd61131df88c919074ffa8136100299)) +* Avoid a saturating add in slice_uncons_while ([ad4180dd](https://github.com/Marwes/combine/commit/ad4180dd7d3530d47502795ead21e13b7816aed7)) + + +  <a name="v4.6.0"></a>  ## v4.6.0 (2021-06-16)   
diff --git a/Cargo.lock b/Cargo.lock index deacbb0..843b88f 100644 --- a/Cargo.lock +++ b/Cargo.lock 
@@ -1,5 +1,7 @@  # This file is automatically @generated by Cargo.  # It is not intended for manual editing. +version = 3 +  [[package]]  name = "aho-corasick"  version = "0.7.15" @@ -266,15 +268,15 @@    [[package]]  name = "combine" -version = "4.6.0" +version = "4.6.1"  dependencies = [  "async-std",  "bytes 0.5.6",  "bytes 1.0.0",  "criterion",  "futures 0.3.8", + "futures-core",  "futures-io", - "futures-util",  "memchr",  "once_cell",  "partial-io", 
diff --git a/Cargo.toml b/Cargo.toml index a3d9dd8..71df3f5 100644 --- a/Cargo.toml +++ b/Cargo.toml 
@@ -13,7 +13,7 @@  [package]  edition = "2018"  name = "combine" -version = "4.6.0" +version = "4.6.1"  authors = ["Markus Westerlind <marwes91@gmail.com>"]  description = "Fast parser combinators on arbitrary streams with zero-copy support."  documentation = "https://docs.rs/combine" @@ -35,7 +35,7 @@    [[example]]  name = "async" -required-features = ["std"] +required-features = ["std", "tokio"]    [[example]]  name = "date" @@ -51,15 +51,17 @@    [[test]]  name = "async" -required-features = ["tokio-02", "futures-util-03"] +required-features = ["tokio-02", "futures-io-03"]    [[bench]]  name = "json"  harness = false +required-features = ["std"]    [[bench]]  name = "http"  harness = false +required-features = ["std"]    [[bench]]  name = "mp4" @@ -74,19 +76,18 @@  optional = true  package = "bytes"   +[dependencies.futures-core-03] +version = "0.3.1" +optional = true +default-features = false +package = "futures-core" +  [dependencies.futures-io-03]  version = "0.3.1"  optional = true  default-features = false  package = "futures-io"   -[dependencies.futures-util-03] -version = "0.3.1" -features = ["io", "std"] -optional = true -default-features = false -package = "futures-util" -  [dependencies.memchr]  version = "2.2"  default-features = false @@ -117,6 +118,12 @@  optional = true  default-features = false  package = "tokio" + +[dependencies.tokio-util] +version = "0.6" +features = ["codec"] +optional = true +default-features = false  [dev-dependencies.async-std]  version = "1"   @@ -163,16 +170,13 @@  features = ["fs", "macros", "rt", "rt-multi-thread", "io-util"]  package = "tokio"   -[dev-dependencies.tokio-util] -version = "0.6" -features = ["codec"] -  [features] +alloc = []  default = ["std"] -futures-03 = ["pin-project", "std", "futures-io-03", "futures-util-03", "pin-project-lite"] +futures-03 = ["pin-project", "std", "futures-core-03", "futures-io-03", "pin-project-lite"]  mp4 = []  pin-project = ["pin-project-lite"] -std = ["memchr/use_std", "bytes"] -tokio = ["tokio-dep", "futures-util-03", "pin-project-lite"] -tokio-02 = ["pin-project", "std", "tokio-02-dep", "futures-util-03", "pin-project-lite", "bytes_05"] -tokio-03 = ["pin-project", "std", "tokio-03-dep", "futures-util-03", "pin-project-lite"] +std = ["memchr/use_std", "bytes", "alloc"] +tokio = ["tokio-dep", "tokio-util/io", "futures-core-03", "pin-project-lite"] +tokio-02 = ["pin-project", "std", "tokio-02-dep", "futures-core-03", "pin-project-lite", "bytes_05"] +tokio-03 = ["pin-project", "std", "tokio-03-dep", "futures-core-03", "pin-project-lite"] 
diff --git a/Cargo.toml.orig b/Cargo.toml.orig index a4448e1..f2517e6 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig 
@@ -1,6 +1,6 @@  [package]  name = "combine" -version = "4.6.0" +version = "4.6.1"  authors = ["Markus Westerlind <marwes91@gmail.com>"]    description = "Fast parser combinators on arbitrary streams with zero-copy support." @@ -34,8 +34,9 @@  tokio-02-dep = { version = "0.2.3", package = "tokio", features = ["io-util"], default-features = false, optional = true }  tokio-03-dep = { version = "0.3", package = "tokio", default-features = false, optional = true }  tokio-dep = { version = "1", package = "tokio", default-features = false, optional = true } +tokio-util = { version = "0.6", features = ["codec"], default-features = false, optional = true } +futures-core-03 = { version = "0.3.1", package = "futures-core", default-features = false, optional = true }  futures-io-03 = { version = "0.3.1", package = "futures-io", default-features = false, optional = true } -futures-util-03 = { version = "0.3.1", package = "futures-util", features = ["io", "std"], default-features = false, optional = true }  bytes_05 = { version = "0.5", package = "bytes", optional = true }  bytes = { version = "1", optional = true }   @@ -49,7 +50,6 @@  tokio-02-dep = { version = "0.2", features = ["fs", "io-driver", "io-util", "macros"], package = "tokio" }  tokio-03-dep = { version = "0.3", features = ["fs", "macros", "rt-multi-thread"], package = "tokio" }  tokio-dep = { version = "1", features = ["fs", "macros", "rt", "rt-multi-thread", "io-util"], package = "tokio" } -tokio-util = { version = "0.6", features = ["codec"] }  partial-io = { version = "0.3", features = ["tokio", "quickcheck"] }  quickcheck = "0.6"  quick-error = "1.0" @@ -60,23 +60,26 @@  # Run the mp4 benchmark, requires a mp4 file named `small.mp4` in the benches directory  mp4 = []  pin-project = ["pin-project-lite"] -tokio-02 = ["pin-project", "std", "tokio-02-dep", "futures-util-03", "pin-project-lite", "bytes_05"] -tokio-03 = ["pin-project", "std", "tokio-03-dep", "futures-util-03", "pin-project-lite"] -tokio = ["tokio-dep", "futures-util-03", "pin-project-lite"] -futures-03 = ["pin-project", "std", "futures-io-03", "futures-util-03", "pin-project-lite"] -std = ["memchr/use_std", "bytes"] +tokio-02 = ["pin-project", "std", "tokio-02-dep", "futures-core-03", "pin-project-lite", "bytes_05"] +tokio-03 = ["pin-project", "std", "tokio-03-dep", "futures-core-03", "pin-project-lite"] +tokio = ["tokio-dep", "tokio-util/io", "futures-core-03", "pin-project-lite"] +futures-03 = ["pin-project", "std", "futures-core-03", "futures-io-03", "pin-project-lite"] +std = ["memchr/use_std", "bytes", "alloc"] +alloc = []    [[test]]  name = "async" -required-features = ["tokio-02", "futures-util-03"] +required-features = ["tokio-02", "futures-io-03"]    [[bench]]  name = "json"  harness = false +required-features = ["std"]    [[bench]]  name = "http"  harness = false +required-features = ["std"]    [[bench]]  name = "mp4" @@ -85,7 +88,7 @@    [[example]]  name = "async" -required-features = ["std"] +required-features = ["std", "tokio"]    [[example]]  name = "date" 
diff --git a/METADATA b/METADATA index 378608f..63cbd25 100644 --- a/METADATA +++ b/METADATA 
@@ -7,13 +7,13 @@  }  url {  type: ARCHIVE - value: "https://static.crates.io/crates/combine/combine-4.6.0.crate" + value: "https://static.crates.io/crates/combine/combine-4.6.1.crate"  } - version: "4.6.0" + version: "4.6.1"  license_type: NOTICE  last_upgrade_date {  year: 2021 - month: 7 - day: 30 + month: 9 + day: 22  }  } 
diff --git a/benches/http.rs b/benches/http.rs index 4330985..68414a5 100644 --- a/benches/http.rs +++ b/benches/http.rs 
@@ -1,3 +1,5 @@ +#![cfg(feature = "std")] +  #[macro_use]  extern crate criterion;  #[macro_use] @@ -29,7 +31,8 @@  }    fn is_token(c: u8) -> bool { - match c { + !matches!( + c,  128..=255  | 0..=31  | b'(' @@ -49,9 +52,8 @@  | b'='  | b'{'  | b'}' - | b' ' => false, - _ => true, - } + | b' ' + )  }    fn is_horizontal_space(c: u8) -> bool { @@ -64,7 +66,7 @@  c != b' '  }  fn is_http_version(c: u8) -> bool { - c >= b'0' && c <= b'9' || c == b'.' + (b'0'..=b'9').contains(&c) || c == b'.'  }    fn end_of_line<'a, Input>() -> impl Parser<Input, Output = u8> @@ -94,9 +96,9 @@  })  }   -fn parse_http_request<'a, Input>( - input: Input, -) -> Result<((Request<'a>, Vec<Header<'a>>), Input), Input::Error> +type HttpRequest<'a> = (Request<'a>, Vec<Header<'a>>); + +fn parse_http_request<'a, Input>(input: Input) -> Result<(HttpRequest<'a>, Input), Input::Error>  where  Input: RangeStream<Token = u8, Range = &'a [u8]>,  Input::Error: ParseError<Input::Token, Input::Range, Input::Position>, @@ -122,7 +124,7 @@  request.parse(input)  }   -static REQUESTS: &'static [u8] = include_bytes!("http-requests.txt"); +static REQUESTS: &[u8] = include_bytes!("http-requests.txt");    fn http_requests_small(b: &mut Bencher<'_>) {  http_requests_bench(b, easy::Stream(REQUESTS)) 
diff --git a/benches/json.rs b/benches/json.rs index 8a44b97..56add8e 100644 --- a/benches/json.rs +++ b/benches/json.rs 
@@ -1,6 +1,8 @@  // `impl Trait` is not required for this parser but we use to to show that it can be used to  // significantly simplify things   +#![cfg(feature = "std")] +  #[macro_use]  extern crate criterion;   @@ -223,7 +225,7 @@  Ok(result) => assert_eq!(result, (expected, "")),  Err(e) => {  println!("{}", e); - assert!(false); + panic!();  }  }  } @@ -241,10 +243,10 @@  let mut parser = json_value();  match parser.easy_parse(position::Stream::new(&data[..])) {  Ok((Value::Array(_), _)) => (), - Ok(_) => assert!(false), + Ok(_) => panic!(),  Err(err) => {  println!("{}", err); - assert!(false); + panic!();  }  }  bencher.iter(|| { @@ -258,10 +260,10 @@  let mut parser = json_value();  match parser.parse(position::Stream::new(&data[..])) {  Ok((Value::Array(_), _)) => (), - Ok(_) => assert!(false), + Ok(_) => panic!(),  Err(err) => {  println!("{}", err); - assert!(false); + panic!();  }  }  bencher.iter(|| { @@ -275,10 +277,10 @@  let mut parser = json_value();  match parser.parse(&data[..]) {  Ok((Value::Array(_), _)) => (), - Ok(_) => assert!(false), + Ok(_) => panic!(),  Err(err) => {  println!("{}", err); - assert!(false); + panic!();  }  }  bencher.iter(|| { @@ -300,10 +302,10 @@  Ok((Value::Array(v), _)) => {  black_box(v);  } - Ok(_) => assert!(false), + Ok(_) => panic!(),  Err(err) => {  println!("{}", err); - assert!(false); + panic!();  }  }  }); 
diff --git a/examples/async.rs b/examples/async.rs index 4ebc8e9..909ca2c 100644 --- a/examples/async.rs +++ b/examples/async.rs 
@@ -1,4 +1,5 @@  #![cfg(feature = "std")] +#![cfg(feature = "tokio")]    use std::{cell::Cell, io::Cursor, rc::Rc, str};   
diff --git a/src/error.rs b/src/error.rs index 854af9d..23326c7 100644 --- a/src/error.rs +++ b/src/error.rs 
@@ -43,6 +43,8 @@  /// `Token`, `Range`, `Format` or `Static`/`&'static str`  pub trait ErrorInfo<'s, T, R> {  type Format: fmt::Display; + + #[allow(clippy::wrong_self_convention)]  fn into_info(&'s self) -> Info<T, R, Self::Format>;  }   @@ -1087,8 +1089,7 @@  CloneOnly { s: "x".to_string() },  CloneOnly { s: "y".to_string() },  ][..]; - let result = - crate::parser::range::take_while(|c: CloneOnly| c.s == "x".to_string()).parse(input); + let result = crate::parser::range::take_while(|c: CloneOnly| c.s == "x").parse(input);  assert_eq!(  result,  Ok(( 
diff --git a/src/future_ext.rs b/src/future_ext.rs new file mode 100644 index 0000000..f6168a3 --- /dev/null +++ b/src/future_ext.rs 
@@ -0,0 +1,29 @@ +use crate::lib::future::Future; +use crate::lib::marker::Unpin; +use crate::lib::pin::Pin; +use crate::lib::task::{Context, Poll}; + +// Replace usage of this with std::future::poll_fn once it stabilizes +pub struct PollFn<F> { + f: F, +} + +impl<F> Unpin for PollFn<F> {} + +pub fn poll_fn<T, F>(f: F) -> PollFn<F> +where + F: FnMut(&mut Context<'_>) -> Poll<T>, +{ + PollFn { f } +} + +impl<T, F> Future for PollFn<F> +where + F: FnMut(&mut Context<'_>) -> Poll<T>, +{ + type Output = T; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> { + (&mut self.f)(cx) + } +} 
diff --git a/src/lib.rs b/src/lib.rs index af2b713..ab46d81 100644 --- a/src/lib.rs +++ b/src/lib.rs 
@@ -195,6 +195,9 @@  #![cfg_attr(not(feature = "std"), no_std)]  #![cfg_attr(docsrs, feature(doc_cfg))]   +#[cfg(feature = "alloc")] +extern crate alloc; +  #[doc(inline)]  pub use crate::error::{ParseError, ParseResult, StdParseResult};   @@ -616,6 +619,9 @@  #[macro_use]  pub mod parser;   +#[cfg(feature = "futures-core-03")] +pub mod future_ext; +  #[doc(hidden)]  #[derive(Clone, PartialOrd, PartialEq, Debug, Copy)]  pub struct ErrorOffset(u8); @@ -700,7 +706,7 @@  }  }   - fn integer<'a, Input>(input: &mut Input) -> StdParseResult<i64, Input> + fn integer<Input>(input: &mut Input) -> StdParseResult<i64, Input>  where  Input: Stream<Token = char>,  Input::Error: ParseError<Input::Token, Input::Range, Input::Position>, @@ -864,11 +870,11 @@  .map(|x| x.to_string())  .or(many1(digit()));  match p.easy_parse(position::Stream::new("le123")) { - Ok(_) => assert!(false), + Ok(_) => panic!(),  Err(err) => assert_eq!(err.position, SourcePosition { line: 1, column: 1 }),  }  match p.easy_parse(position::Stream::new("let1")) { - Ok(_) => assert!(false), + Ok(_) => panic!(),  Err(err) => assert_eq!(err.position, SourcePosition { line: 1, column: 4 }),  }  } 
diff --git a/src/parser/byte.rs b/src/parser/byte.rs index 95fc4c1..b28d362 100644 --- a/src/parser/byte.rs +++ b/src/parser/byte.rs 
@@ -220,7 +220,7 @@  Input: Stream<Token = u8>,  Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,  { - satisfy(|ch| ch >= b'0' && ch <= b'7').expected("octal digit") + satisfy(|ch| (b'0'..=b'7').contains(&ch)).expected("octal digit")  }    /// Parses an ASCII hexdecimal digit (accepts both uppercase and lowercase). @@ -599,7 +599,7 @@  pub F64, f64, be_f64, le_f64, read_f64  );   - #[cfg(test)] + #[cfg(all(feature = "std", test))]  mod tests {    use crate::stream::{buffered, position, IteratorStream}; @@ -641,7 +641,7 @@  }  }   -#[cfg(test)] +#[cfg(all(feature = "std", test))]  mod tests {    use crate::stream::{buffered, position, read}; 
diff --git a/src/parser/choice.rs b/src/parser/choice.rs index 8efdb0c..ef4e5a8 100644 --- a/src/parser/choice.rs +++ b/src/parser/choice.rs 
@@ -835,7 +835,7 @@  }  }   -#[cfg(test)] +#[cfg(all(feature = "std", test))]  mod tests {    use crate::parser::{token::any, EasyParser}; 
diff --git a/src/parser/combinator.rs b/src/parser/combinator.rs index d8f3f25..bb06b89 100644 --- a/src/parser/combinator.rs +++ b/src/parser/combinator.rs 
@@ -12,6 +12,12 @@  Parser,  };   +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, string::String, vec::Vec}; + +#[cfg(feature = "alloc")] +use crate::lib::any::Any; +  #[derive(Copy, Clone)]  pub struct NotFollowedBy<P>(P);  impl<Input, O, P> Parser<Input> for NotFollowedBy<P> @@ -711,16 +717,16 @@  Ignore(p)  }   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  #[derive(Default)] -pub struct AnyPartialState(Option<Box<dyn std::any::Any>>); +pub struct AnyPartialState(Option<Box<dyn Any>>);   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  pub struct AnyPartialStateParser<P>(P);   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  impl<Input, P> Parser<Input> for AnyPartialStateParser<P>  where  Input: Stream, @@ -803,7 +809,7 @@  ///  /// # }  /// ``` -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  pub fn any_partial_state<Input, P>(p: P) -> AnyPartialStateParser<P>  where @@ -814,16 +820,16 @@  AnyPartialStateParser(p)  }   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  #[derive(Default)] -pub struct AnySendPartialState(Option<Box<dyn std::any::Any + Send>>); +pub struct AnySendPartialState(Option<Box<dyn Any + Send>>);   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  pub struct AnySendPartialStateParser<P>(P);   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  impl<Input, P> Parser<Input> for AnySendPartialStateParser<P>  where  Input: Stream, @@ -906,7 +912,7 @@  ///  /// # }  /// ``` -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  pub fn any_send_partial_state<Input, P>(p: P) -> AnySendPartialStateParser<P>  where @@ -917,16 +923,16 @@  AnySendPartialStateParser(p)  }   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  #[derive(Default)] -pub struct AnySendSyncPartialState(Option<Box<dyn std::any::Any + Send + Sync>>); +pub struct AnySendSyncPartialState(Option<Box<dyn Any + Send + Sync>>);   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  pub struct AnySendSyncPartialStateParser<P>(P);   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  impl<Input, P> Parser<Input> for AnySendSyncPartialStateParser<P>  where  Input: Stream, @@ -1008,7 +1014,7 @@  ///  /// # }  /// ``` -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  pub fn any_send_sync_partial_state<Input, P>(p: P) -> AnySendSyncPartialStateParser<P>  where @@ -1179,52 +1185,52 @@  use self::internal::Sealed;    pub trait StrLike: Sealed { - fn from_utf8(&self) -> Result<&str, ()>; + fn from_utf8(&self) -> Option<&str>;  }   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  impl Sealed for String {} -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  impl StrLike for String { - fn from_utf8(&self) -> Result<&str, ()> { - Ok(self) + fn from_utf8(&self) -> Option<&str> { + Some(self)  }  }    impl<'a> Sealed for &'a str {}  impl<'a> StrLike for &'a str { - fn from_utf8(&self) -> Result<&str, ()> { - Ok(*self) + fn from_utf8(&self) -> Option<&str> { + Some(*self)  }  }    impl Sealed for str {}  impl StrLike for str { - fn from_utf8(&self) -> Result<&str, ()> { - Ok(self) + fn from_utf8(&self) -> Option<&str> { + Some(self)  }  }   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  impl Sealed for Vec<u8> {} -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  impl StrLike for Vec<u8> { - fn from_utf8(&self) -> Result<&str, ()> { + fn from_utf8(&self) -> Option<&str> {  (**self).from_utf8()  }  }    impl<'a> Sealed for &'a [u8] {}  impl<'a> StrLike for &'a [u8] { - fn from_utf8(&self) -> Result<&str, ()> { + fn from_utf8(&self) -> Option<&str> {  (**self).from_utf8()  }  }    impl Sealed for [u8] {}  impl StrLike for [u8] { - fn from_utf8(&self) -> Result<&str, ()> { - str::from_utf8(self).map_err(|_| ()) + fn from_utf8(&self) -> Option<&str> { + str::from_utf8(self).ok()  }  }   @@ -1275,7 +1281,7 @@  {  parser.and_then(|r| {  r.from_utf8() - .map_err(|_| StreamErrorFor::<Input>::expected_static_message("UTF-8")) + .ok_or_else(|| StreamErrorFor::<Input>::expected_static_message("UTF-8"))  .and_then(|s| s.parse().map_err(StreamErrorFor::<Input>::message_format))  })  } 
diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 98a1e03..17a88b7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs 
@@ -27,6 +27,9 @@  sequence::{skip, with, Skip, With},  };   +#[cfg(feature = "alloc")] +use alloc::boxed::Box; +  /// Internal API. May break without a semver bump  #[macro_export]  #[doc(hidden)] @@ -846,7 +849,7 @@  /// assert_eq!(result, Ok((('a', 'c'), "")));  /// # }  /// ``` - #[cfg(feature = "std")] + #[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  fn boxed<'a>(  self, @@ -1086,7 +1089,7 @@  forward_deref!(Input);  }   -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  impl<P, Input> Parser<Input> for Box<P>  where  P: ?Sized + Parser<Input>, 
diff --git a/src/parser/sequence.rs b/src/parser/sequence.rs index b296966..1c8bdcb 100644 --- a/src/parser/sequence.rs +++ b/src/parser/sequence.rs 
@@ -179,6 +179,7 @@  }  };  state.offset = $h.parser_count().0.saturating_add(1); + // SAFETY: must be set to avoid UB below when unwrapping  state.$h.value = Some(temp);    // Once we have successfully parsed the partial input we may resume parsing in @@ -211,6 +212,7 @@  }  };  state.offset = state.offset.saturating_add($id.parser_count().0); + // SAFETY: must be set to avoid UB below when unwrapping  state.$id.value = Some(temp);    // Once we have successfully parsed the partial input we may resume parsing in @@ -219,6 +221,7 @@  }  )*   + // SAFETY: requires both $h and $id to be set, see previous SAFETY comments  let value = unsafe { (state.$h.unwrap_value(), $(state.$id.unwrap_value()),*) };  if first_empty_parser != 0 {  CommitOk(value) @@ -785,7 +788,7 @@  ThenPartial(p, f)  }   -#[cfg(test)] +#[cfg(all(feature = "std", test))]  mod tests {    use crate::parser::{token::any, EasyParser}; 
diff --git a/src/stream/buf_reader.rs b/src/stream/buf_reader.rs index 1a10bc9..512833d 100644 --- a/src/stream/buf_reader.rs +++ b/src/stream/buf_reader.rs 
@@ -8,7 +8,7 @@  ))]  use std::{mem::MaybeUninit, pin::Pin};   -#[cfg(feature = "futures-util-03")] +#[cfg(feature = "futures-core-03")]  use std::task::{Context, Poll};    #[cfg(feature = "futures-03")] @@ -25,8 +25,8 @@  #[cfg(feature = "tokio")]  use tokio_dep::io::AsyncBufRead as _;   -#[cfg(feature = "futures-util-03")] -use futures_util_03::ready; +#[cfg(feature = "futures-core-03")] +use futures_core_03::ready;    #[cfg(feature = "pin-project-lite")]  pin_project! { @@ -208,7 +208,7 @@  #[cfg(feature = "futures-03")]  impl<R> CombineAsyncRead<R> for Buffer  where - R: futures_util_03::io::AsyncRead, + R: futures_io_03::AsyncRead,  {  fn poll_extend_buf(  &mut self, @@ -250,6 +250,17 @@  }    #[cfg(feature = "tokio-03")] +fn tokio_03_to_read_buf(bs: &mut BytesMut) -> tokio_03_dep::io::ReadBuf<'_> { + let uninit = bs.chunk_mut(); + unsafe { + tokio_03_dep::io::ReadBuf::uninit(std::slice::from_raw_parts_mut( + uninit.as_mut_ptr() as *mut MaybeUninit<u8>, + uninit.len(), + )) + } +} + +#[cfg(feature = "tokio-03")]  impl<R> CombineRead<R, dyn tokio_03_dep::io::AsyncRead> for Buffer  where  R: tokio_03_dep::io::AsyncRead, @@ -259,22 +270,7 @@  cx: &mut Context<'_>,  read: Pin<&mut R>,  ) -> Poll<io::Result<usize>> { - if !self.0.has_remaining_mut() { - self.0.reserve(8 * 1024); - } - let uninit = self.0.chunk_mut(); - let mut buf = unsafe { - tokio_03_dep::io::ReadBuf::uninit(std::slice::from_raw_parts_mut( - uninit.as_mut_ptr() as *mut MaybeUninit<u8>, - uninit.len(), - )) - }; - ready!(read.poll_read(cx, &mut buf))?; - let n = buf.filled().len(); - unsafe { - self.0.advance_mut(n); - } - Poll::Ready(Ok(n)) + tokio_03_read_buf(cx, read, &mut self.0)  }  }   @@ -288,13 +284,9 @@  bs.reserve(8 * 1024);  }   + let mut buf = tokio_03_to_read_buf(bs); + ready!(read.poll_read(cx, &mut buf))?;  unsafe { - let uninit = bs.chunk_mut(); - let mut buf = tokio_03_dep::io::ReadBuf::uninit(std::slice::from_raw_parts_mut( - uninit.as_mut_ptr() as *mut MaybeUninit<u8>, - uninit.len(), - )); - ready!(read.poll_read(cx, &mut buf))?;  let n = buf.filled().len();  bs.advance_mut(n);  Poll::Ready(Ok(n)) @@ -311,44 +303,21 @@  cx: &mut Context<'_>,  read: Pin<&mut R>,  ) -> Poll<io::Result<usize>> { - if !self.0.has_remaining_mut() { - self.0.reserve(8 * 1024); - } - let mut buf = unsafe { - tokio_dep::io::ReadBuf::uninit( - &mut *(self.0.chunk_mut() as *mut _ as *mut [MaybeUninit<u8>]), - ) - }; - ready!(read.poll_read(cx, &mut buf))?; - let n = buf.filled().len(); - unsafe { - self.0.advance_mut(n); - } - Poll::Ready(Ok(n)) + tokio_read_buf(read, cx, &mut self.0)  }  }    #[cfg(feature = "tokio")]  fn tokio_read_buf( - cx: &mut Context<'_>,  read: Pin<&mut impl tokio_dep::io::AsyncRead>, + cx: &mut Context<'_>,  bs: &mut bytes::BytesMut,  ) -> Poll<io::Result<usize>> {  if !bs.has_remaining_mut() {  bs.reserve(8 * 1024);  }   - unsafe { - let uninit = bs.chunk_mut(); - let mut buf = tokio_dep::io::ReadBuf::uninit(std::slice::from_raw_parts_mut( - uninit.as_mut_ptr() as *mut MaybeUninit<u8>, - uninit.len(), - )); - ready!(read.poll_read(cx, &mut buf))?; - let n = buf.filled().len(); - bs.advance_mut(n); - Poll::Ready(Ok(n)) - } + tokio_util::io::poll_read_buf(read, cx, bs)  }    /// Marker used by `Decoder` for an external buffer @@ -389,27 +358,30 @@  buf.reserve(8 * 1024);  }   - // Copy of tokio's read_buf method (but it has to force initialize the buffer) - let copied = unsafe { - let n = { - let bs = buf.chunk_mut(); + // Copy of tokio's poll_read_buf method (but it has to force initialize the buffer) + let n = { + let bs = buf.chunk_mut();   - for i in 0..bs.len() { - bs.write_byte(i, 0); - } + for i in 0..bs.len() { + bs.write_byte(i, 0); + }   - // Convert to `&mut [u8]` - let bs = &mut *(bs as *mut _ as *mut [u8]); + // Convert to `&mut [u8]` + // SAFETY: the entire buffer is preinitialized above + let bs = unsafe { &mut *(bs as *mut _ as *mut [u8]) };   - let n = read.read(bs)?; - assert!(n <= bs.len(), "AsyncRead reported that it initialized more than the number of bytes in the buffer"); - n - }; - - buf.advance_mut(n); + let n = read.read(bs)?; + assert!( + n <= bs.len(), + "AsyncRead reported that it initialized more than the number of bytes in the buffer" + );  n  }; - Ok(copied) + + // SAFETY: the entire buffer has been preinitialized + unsafe { buf.advance_mut(n) }; + + Ok(n)  }    #[cfg(feature = "tokio-02")] @@ -475,14 +447,14 @@  ) -> Poll<io::Result<usize>> {  let me = read.project();   - tokio_read_buf(cx, me.inner, me.buf) + tokio_read_buf(me.inner, cx, me.buf)  }  }    #[cfg(feature = "futures-03")]  impl<R> CombineAsyncRead<BufReader<R>> for Bufferless  where - R: futures_util_03::io::AsyncRead, + R: futures_io_03::AsyncRead,  {  fn poll_extend_buf(  &mut self, @@ -520,24 +492,30 @@  read: Pin<&mut R>,  ) -> Poll<io::Result<usize>>  where - R: futures_util_03::io::AsyncRead, + R: futures_io_03::AsyncRead,  {  // Copy of tokio's read_buf method (but it has to force initialize the buffer) - let copied = unsafe { - let n = { - let bs = buf.chunk_mut(); - // Convert to `&mut [u8]` - let bs = &mut *(bs as *mut _ as *mut [u8]); + let n = { + let bs = buf.chunk_mut(); + // preinit the buffer + for i in 0..bs.len() { + bs.write_byte(i, 0); + }   - let n = ready!(read.poll_read(cx, bs))?; - assert!(n <= bs.len(), "AsyncRead reported that it initialized more than the number of bytes in the buffer"); - n - }; + // Convert to `&mut [u8]` + // SAFETY: preinitialize the buffer + let bs = unsafe { &mut *(bs as *mut _ as *mut [u8]) };   - buf.advance_mut(n); + let n = ready!(read.poll_read(cx, bs))?; + assert!( + n <= bs.len(), + "AsyncRead reported that it initialized more than the number of bytes in the buffer" + );  n  }; - Poll::Ready(Ok(copied)) + // SAFETY: the buffer was preinitialized + unsafe { buf.advance_mut(n) }; + Poll::Ready(Ok(n))  }    #[cfg(feature = "tokio-02")] @@ -714,7 +692,7 @@  // If we've reached the end of our internal buffer then we need to fetch  // some more data from the underlying reader.  if me.buf.is_empty() { - ready!(tokio_read_buf(cx, me.inner, me.buf))?; + ready!(tokio_read_buf(me.inner, cx, me.buf))?;  }  Poll::Ready(Ok(&me.buf[..]))  } @@ -800,8 +778,7 @@    impl<R: AsyncRead> BufReader<R> {  async fn extend_buf_tokio_02(mut self: Pin<&mut Self>) -> io::Result<usize> { - futures_util_03::future::poll_fn(|cx| Bufferless.poll_extend_buf(cx, self.as_mut())) - .await + crate::future_ext::poll_fn(|cx| Bufferless.poll_extend_buf(cx, self.as_mut())).await  }  }   @@ -841,7 +818,7 @@  #[tokio::test]  async fn buf_reader_extend_buf() {  let read = BufReader::with_capacity(3, &[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 0][..]); - futures_util_03::pin_mut!(read); + futures_03_dep::pin_mut!(read);    assert_eq!(read.as_mut().extend_buf_tokio_02().await.unwrap(), 3);  assert_eq!(read.buffer(), [1, 2, 3]); @@ -868,8 +845,7 @@    impl<R: AsyncRead> BufReader<R> {  async fn extend_buf_tokio(mut self: Pin<&mut Self>) -> io::Result<usize> { - futures_util_03::future::poll_fn(|cx| Bufferless.poll_extend_buf(cx, self.as_mut())) - .await + crate::future_ext::poll_fn(|cx| Bufferless.poll_extend_buf(cx, self.as_mut())).await  }  }   @@ -909,7 +885,7 @@  #[tokio::test]  async fn buf_reader_extend_buf() {  let read = BufReader::with_capacity(3, &[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 0][..]); - futures_util_03::pin_mut!(read); + futures_03_dep::pin_mut!(read);    assert_eq!(read.as_mut().extend_buf_tokio().await.unwrap(), 3);  assert_eq!(read.buffer(), [1, 2, 3]); @@ -926,6 +902,7 @@  use std::io::Read;    #[test] + #[allow(clippy::unused_io_amount)]  fn buf_reader() {  let mut read = BufReader::with_capacity(3, &[1u8, 2, 3, 4, 5, 6, 7, 8, 9, 0][..]);   
diff --git a/src/stream/buffered.rs b/src/stream/buffered.rs index 93f7b82..32caba2 100644 --- a/src/stream/buffered.rs +++ b/src/stream/buffered.rs 
@@ -1,4 +1,4 @@ -use std::collections::VecDeque; +use alloc::collections::VecDeque;    use crate::{  error::StreamError, 
diff --git a/src/stream/decoder.rs b/src/stream/decoder.rs index 9807d80..ad034f8 100644 --- a/src/stream/decoder.rs +++ b/src/stream/decoder.rs 
@@ -161,7 +161,7 @@  C: crate::stream::buf_reader::CombineRead<R, dyn tokio_02_dep::io::AsyncRead>,  {  let copied = - futures_util_03::future::poll_fn(|cx| self.buffer.poll_extend_buf(cx, reader.as_mut())) + crate::future_ext::poll_fn(|cx| self.buffer.poll_extend_buf(cx, reader.as_mut()))  .await?;  if copied == 0 {  self.end_of_input = true; @@ -180,7 +180,7 @@  C: crate::stream::buf_reader::CombineRead<R, dyn tokio_03_dep::io::AsyncRead>,  {  let copied = - futures_util_03::future::poll_fn(|cx| self.buffer.poll_extend_buf(cx, reader.as_mut())) + crate::future_ext::poll_fn(|cx| self.buffer.poll_extend_buf(cx, reader.as_mut()))  .await?;  if copied == 0 {  self.end_of_input = true; @@ -199,7 +199,7 @@  C: crate::stream::buf_reader::CombineRead<R, dyn tokio_dep::io::AsyncRead>,  {  let copied = - futures_util_03::future::poll_fn(|cx| self.buffer.poll_extend_buf(cx, reader.as_mut())) + crate::future_ext::poll_fn(|cx| self.buffer.poll_extend_buf(cx, reader.as_mut()))  .await?;  if copied == 0 {  self.end_of_input = true; 
diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f38bd72..803e9a8 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs 
@@ -52,7 +52,7 @@  pub mod buf_reader;  /// Stream wrapper which provides a `ResetStream` impl for `StreamOnce` impls which do not have  /// one. -#[cfg(feature = "std")] +#[cfg(feature = "alloc")]  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]  pub mod buffered;  #[cfg(feature = "std")] @@ -576,7 +576,7 @@  }  match s.as_bytes().get(index) {  None => false, - Some(&b) => b < 128 || b >= 192, + Some(b) => !(128..=192).contains(b),  }  }  if size <= self.len() { @@ -616,12 +616,21 @@  }  }   -fn slice_uncons_while<'a, T, F>(slice: &mut &'a [T], mut i: usize, mut f: F) -> &'a [T] +#[repr(usize)] +enum UnconsStart { + Zero = 0, + One = 1, +} + +fn slice_uncons_while<'a, T, F>(slice: &mut &'a [T], start: UnconsStart, mut f: F) -> &'a [T]  where  F: FnMut(T) -> bool,  T: Clone,  { + let mut i = start as usize;  let len = slice.len(); + // SAFETY: We only call this function with `One` if the slice has length >= 1 + debug_assert!(len >= i, "");  let mut found = false;    macro_rules! check { @@ -634,6 +643,7 @@  };  }   + // SAFETY: ensures we can access at least 8 elements starting at i, making get_unchecked sound.  while len - i >= 8 {  check!();  check!(); @@ -679,7 +689,7 @@  where  F: FnMut(Self::Token) -> bool,  { - Ok(slice_uncons_while(self, 0, f)) + Ok(slice_uncons_while(self, UnconsStart::Zero, f))  }    #[inline] @@ -698,7 +708,7 @@  }  }   - CommitOk(slice_uncons_while(self, 1, f)) + CommitOk(slice_uncons_while(self, UnconsStart::One, f))  }    #[inline] @@ -1060,11 +1070,14 @@  }  }   -fn slice_uncons_while_ref<'a, T, F>(slice: &mut &'a [T], mut i: usize, mut f: F) -> &'a [T] +fn slice_uncons_while_ref<'a, T, F>(slice: &mut &'a [T], start: UnconsStart, mut f: F) -> &'a [T]  where  F: FnMut(&'a T) -> bool,  { + let mut i = start as usize;  let len = slice.len(); + // SAFETY: We only call this function with `One` if the slice has length >= 1 + debug_assert!(len >= i, "");  let mut found = false;    macro_rules! check { @@ -1077,6 +1090,7 @@  };  }   + // SAFETY: ensures we can access at least 8 elements starting at i, making get_unchecked sound.  while len - i >= 8 {  check!();  check!(); @@ -1122,7 +1136,7 @@  where  F: FnMut(Self::Token) -> bool,  { - Ok(slice_uncons_while_ref(&mut self.0, 0, f)) + Ok(slice_uncons_while_ref(&mut self.0, UnconsStart::Zero, f))  }    #[inline] @@ -1139,7 +1153,7 @@  None => return PeekErr(Tracked::from(UnexpectedParse::Eoi)),  }   - CommitOk(slice_uncons_while_ref(&mut self.0, 1, f)) + CommitOk(slice_uncons_while_ref(&mut self.0, UnconsStart::One, f))  }    #[inline] @@ -1877,7 +1891,7 @@  input.uncons().unwrap();  assert_eq!(input.distance(&before), 2);   - input.reset(before.clone()).unwrap(); + input.reset(before).unwrap();  assert_eq!(input.distance(&before), 0);  }  } 
diff --git a/tests/parser.rs b/tests/parser.rs index c2ab07c..4721825 100644 --- a/tests/parser.rs +++ b/tests/parser.rs 
@@ -1,15 +1,12 @@  use combine::{  parser::{ - byte::bytes_cmp, - char::{digit, letter, string, string_cmp}, - choice::{choice, optional}, - combinator::{attempt, no_partial, not_followed_by}, - error::unexpected, - range::{self, range}, - repeat::{count, count_min_max, many, sep_by, sep_end_by1, skip_until, take_until}, - token::{any, eof, position, token, value, Token}, + char::{digit, letter}, + choice::choice, + combinator::not_followed_by, + range::range, + token::{any, eof, token, Token},  }, - EasyParser, Parser, + Parser,  };    #[test] @@ -45,21 +42,25 @@    #[cfg(feature = "std")]  mod tests_std { - - use combine::{ - parser::{ - byte::{alpha_num, bytes, num::be_u32}, - char::{char, digit, letter}, - }, - stream::{ - easy::{self, Error, Errors}, - position::{self, SourcePosition}, - }, - Parser, - }; -  use super::*;   + use combine::easy::{Error, Errors}; + use combine::parser::byte::alpha_num; + use combine::parser::byte::bytes; + use combine::parser::byte::bytes_cmp; + use combine::parser::byte::num::be_u32; + use combine::parser::char::char; + use combine::parser::char::{string, string_cmp}; + use combine::parser::combinator::no_partial; + use combine::parser::range; + use combine::parser::repeat::{skip_until, take_until}; + use combine::stream::position; + use combine::stream::position::SourcePosition; + use combine::{ + attempt, count, count_min_max, easy, many, optional, position, sep_by, sep_end_by1, + unexpected, value, EasyParser, + }; +  #[derive(Clone, PartialEq, Debug)]  struct CloneOnly {  s: String, @@ -85,8 +86,10 @@    #[test]  fn sep_by_committed_error() { + type TwoLettersList = Vec<(char, char)>; +  let mut parser2 = sep_by((letter(), letter()), token(',')); - let result_err: Result<(Vec<(char, char)>, &str), easy::ParseError<&str>> = + let result_err: Result<(TwoLettersList, &str), easy::ParseError<&str>> =  parser2.easy_parse("a,bc");  assert!(result_err.is_err());  } @@ -619,7 +622,7 @@    #[test]  fn lifetime_inference() { - fn _string<'a>(source: &'a str) { + fn _string(source: &str) {  range::take(1).or(string("a")).parse(source).ok();  range::take(1)  .or(string_cmp("a", |x, y| x == y)) @@ -628,7 +631,7 @@  let _: &'static str = string("a").parse(source).unwrap().0;  let _: &'static str = string_cmp("a", |x, y| x == y).parse(source).unwrap().0;  } - fn _bytes<'a>(source: &'a [u8]) { + fn _bytes(source: &[u8]) {  range::take(1).or(bytes(&[0u8])).parse(source).ok();  range::take(1)  .or(bytes_cmp(&[0u8], |x, y| x == y)) 
diff --git a/tests/parser_macro.rs b/tests/parser_macro.rs index e6d8dfc..2832286 100644 --- a/tests/parser_macro.rs +++ b/tests/parser_macro.rs 
@@ -1,3 +1,5 @@ +#![allow(clippy::single_match)] +  #[macro_use]  extern crate combine;   
diff --git a/travis.sh b/travis.sh index 6d83c38..6397319 100755 --- a/travis.sh +++ b/travis.sh 
@@ -11,6 +11,9 @@    cargo "$@" test --bench json --bench http -- --test  cargo "$@" check --bench mp4 --features mp4 +  + cargo "$@" build --no-default-features --features alloc + cargo "$@" test --no-default-features --features alloc --examples    cargo "$@" build --no-default-features  cargo "$@" test --no-default-features --examples